import path from 'path';
import chalk from 'chalk';
import type { Plugin } from 'rollup';
import { throttle, last } from 'lodash';
import type { Logger } from '../logger';

const enum WriteType {
  JS,
  CSS,
  ASSET,
  HTML,
  SOURCE_MAP,
}

const writeColors = {
  [WriteType.JS]: chalk.cyan,
  [WriteType.CSS]: chalk.magenta,
  [WriteType.ASSET]: chalk.green,
  [WriteType.HTML]: chalk.blue,
  [WriteType.SOURCE_MAP]: chalk.gray,
};

interface BuildReporterPluginParams {
  root: string;
  outDir: string;
  logger: Logger;
}

export function buildReporterPlugin({ root, outDir, logger }: BuildReporterPluginParams): Plugin {
  function printFileInfo(
    filePath: string,
    content: string | Uint8Array,
    type: WriteType,
    maxLength: number,
  ) {
    const kib = content.length / 1024;
    const outDirLog = chalk.gray(chalk.white.dim(`${outDir}/`));
    const fileTypeLog = writeColors[type](filePath.padEnd(maxLength + 2));
    const sizeLog = chalk.blue(`${kib.toFixed(2)}KiB`);

    logger.info(`${outDirLog}${fileTypeLog} ${sizeLog}`);
  }

  const tty = process.stdout.isTTY && !process.env.CI;
  const start = Date.now();
  let hasTransformed = false;
  let hasRenderedChunk = false;
  let transformedCount = 0;
  let chunkCount = 0;

  const logTransform = throttle((id: string) => {
    writeLine(`构建中 (${chalk.blue(transformedCount)}) ${chalk.dim(path.relative(root, id))}`);
  });

  return {
    name: 'seepfly:reporter',

    transform(code, id) {
      transformedCount += 1;
      if (!tty) {
        if (!hasTransformed) {
          logger.info(`构建中...`);
        }
      } else {
        if (id.includes(`?`)) {
          return null;
        }
        logTransform(id);
      }
      hasTransformed = true;
      return null;
    },

    buildEnd() {
      if (tty) {
        process.stdout.clearLine(0);
        process.stdout.cursorTo(0);
      }
      logger.info(`${chalk.green(`✓`)} 已构建 ${chalk.blue(transformedCount)} 个模块`);
    },

    renderStart() {
      chunkCount = 0;
    },

    renderChunk() {
      chunkCount += 1;
      if (!tty) {
        if (!hasRenderedChunk) {
          logger.info('渲染 chunks...');
        }
      } else {
        writeLine(`渲染 chunks (${chalk.blue(chunkCount)})...`);
      }
      hasRenderedChunk = true;
      return null;
    },

    generateBundle() {
      if (tty) {
        process.stdout.clearLine(0);
        process.stdout.cursorTo(0);
      }
    },

    async writeBundle(_, output) {
      const outputKeys = Object.keys(output);
      const longest = Math.max(...outputKeys.map((n) => output[n].fileName.length)) || 0;

      await Promise.all(
        outputKeys.map(async (file) => {
          const chunk = output[file];
          if (chunk.type === 'chunk') {
            const log = async () => {
              printFileInfo(chunk.fileName, chunk.code, WriteType.JS, longest);
              if (chunk.map) {
                printFileInfo(
                  `${chunk.fileName}.map`,
                  chunk.map.toString(),
                  WriteType.SOURCE_MAP,
                  longest,
                );
              }
            };
            await log();
          } else if (chunk.source) {
            const isCSS = chunk.fileName.endsWith('.css');
            printFileInfo(
              chunk.fileName,
              chunk.source,
              isCSS ? WriteType.CSS : WriteType.ASSET,
              longest,
            );
          }
        }),
      );

      const totalTime = (Date.now() - start) / 1000;
      logger.info(`${chalk.green(`✓`)} 构建完成！${tipMessage(totalTime)}`);
    },
  };
}

function writeLine(output: string) {
  process.stdout.clearLine(0);
  process.stdout.cursorTo(0);
  if (output.length < process.stdout.columns) {
    process.stdout.write(output);
  } else {
    process.stdout.write(output.substring(0, process.stdout.columns - 1));
  }
}

function tipMessage(totalTime: number) {
  const timeText = Math.ceil(totalTime);

  const greenTime = chalk.green(timeText);
  const warningTime = chalk.yellow(timeText);
  const redMinute = chalk.red(Math.floor(timeText / 60));
  const redSecond = chalk.red(Math.floor(timeText % 60));

  const messageList = [
    `用时 ${greenTime} 秒，${chalk.green('你是在坐火箭吗？')}`,
    `用时 ${greenTime} 秒，${chalk.green('我就是那最靓的仔！')}`,
    `用了 ${warningTime} 秒，${chalk.yellow('电脑声音呼呼作响！')}`,
    `用了 ${warningTime} 秒，${chalk.yellow('睡一觉起来发现还在编译！')}`,
    `竟然用了 ${redMinute} 分 ${redSecond} 秒，${chalk.red('可以在电脑上煎鸡蛋了！')}`,
    `竟然用了 ${redMinute} 分 ${redSecond} 秒，${chalk.red('我怀疑你用的是小霸王！')}`,
    `竟然花了 ${redMinute} 分 ${redSecond} 秒，${chalk.red('小霸王扔掉吧，没救了！')}`,
  ];
  const step = Math.floor(totalTime / 30);

  return messageList[step] || last(messageList);
}
